home *** CD-ROM | disk | FTP | other *** search
/ Internet Publisher's Toolbox 2.0 / Internet Publisher's Toolbox.iso / html / programs / cgi.bas < prev    next >
Encoding:
BASIC Source File  |  1995-02-18  |  27.0 KB  |  700 lines

  1. '----------------------------------------------------------------------
  2. '       ***********
  3. '       * CGI.BAS *
  4. '       ***********
  5. '
  6. ' VERSION: 1.0  (November 12, 1994)
  7. '
  8. ' AUTHOR:  Robert B. Denny <rdenny@netcom.com>
  9. '
  10. ' Common routines needed to establish a VB environment for
  11. ' CGI "scripts" that run behind the Windows Web Server.
  12. '
  13. ' INTRODUCTION
  14. '
  15. ' The Common Gateway Interface (CGI) version 1.1 specifies a minimal
  16. ' set of data that is made available to the back-end application by
  17. ' an HTTP (Web) server. It also specifies the details for passing this
  18. ' information to the back-end. The latter part of the CGI spec is
  19. ' specific to Unix-like environments. The NCSA httpd for Windows does
  20. ' supply the data items (and more) specified by CGI/1.1, however it
  21. ' uses a different method for passing the data to the back-end.
  22. '
  23. ' DEVELOPMENT
  24. '
  25. ' Windows httpd requires any Windows back-end program to be an
  26. ' executable image. This means that you must convert your VB
  27. ' application into an executable (.EXE) before it can be tested
  28. ' with the server.
  29. '
  30. ' ENVIRONMENT
  31. '
  32. ' The Windows httpd server executes script requests by doing a
  33. ' WinExec with a command line in the following form:
  34. '
  35. '   prog-name cgi-profile input-file output-file url-args
  36. '
  37. ' Assuming you are familiar with the CGI specification, the above
  38. ' should be "intuitively obvious" except for the cgi-profile, which
  39. ' is described in the next section.
  40. '
  41. ' THE CGI PROFILE FILE
  42. '
  43. ' The Unix CGI passes data to the back end by defining environment
  44. ' variables which can be used by shell scripts. The Windows httpd
  45. ' server passes data to its back end via the profile file. The
  46. ' format of the profile is that of a Windows ".INI" file. The keyword
  47. ' names have been changed cosmetically.
  48. '
  49. ' There are 7 sections in a CGI profile file, [CGI], [Accept],
  50. ' [System], [Extra Headers], and [Form Literal], [Form External],
  51. ' and [Form huge]. They are described below:
  52. '
  53. ' [CGI]                <== The standard CGI variables
  54. ' CGI Version=         The version of CGI spoken by the server
  55. ' Request Protocol=    The server's info protocol (e.g. HTTP/1.0)
  56. ' Request Method=      The method specified in the request (e.g., "GET")
  57. ' Executable Path=     Physical pathname of the back-end (this program)
  58. ' Logical Path=        Extra path info in logical space
  59. ' Physical Path=       Extra path info in local physical space
  60. ' Query String=        String following the "?" in the request URL
  61. ' Content Type=        MIME content type of info supplied with request
  62. ' Content Length=      Length, bytes, of info supplied with request
  63. ' Server Software=     Version/revision of the info (HTTP) server
  64. ' Server Name=         Server's network hostname (or alias from config)
  65. ' Server Port=         Server's network port number
  66. ' Server Admin=        E-Mail address of server's admin. (config)
  67. ' Remote Host=         Remote client's network hostname
  68. ' Remote Address=      Remote client's network address
  69. ' Authenticated Username=Username used for restricted access
  70. ' Authentication Method=Method used for authentication (e.g., "Basic")
  71. ' RFC-931 Identity=    TAP identity of client user
  72. '
  73. ' [Accept]             <== What the client says it can take
  74. ' The MIME types found in the request header as
  75. '    Accept: xxx/yyy; zzzz...
  76. ' are entered in this section as
  77. '    xxx/yyy=zzzz...
  78. ' If only the MIME type appears, the form is
  79. '    xxx/yyy=Yes
  80. '
  81. ' [System]             <== Windows interface specifics
  82. ' Output File=         Pathname of file to receive results
  83. ' Content File=        Pathname of file containing request content (raw)
  84. ' Debug Mode=          If server's back-end debug flag is set (Yes/No)
  85. '
  86. ' [Extra Headers]
  87. ' Any "extra" headers found in the request that activated this
  88. ' program. They are listed in "key=value" form. Usually, you'll see
  89. ' at least the name of the browser here.
  90. '
  91. ' [Form Literal]
  92. ' If the request was a POST from a Mosaic form (with content type of
  93. ' "application/x-www-form-urlencoded"), the server will decode the
  94. ' form data. Raw form input is of the form "key=value&key=value&...",
  95. ' with the value parts "URL-encoded". The server splits the key=value
  96. ' pairs at the '&', then spilts the key and value at the '=',
  97. ' URL-decodes the value string and puts the result into key=value
  98. ' (decoded) form in the [Form Literal] section of the INI.
  99. '
  100. ' [Form External]
  101. ' If the decoded value string is more than 254 characters long,
  102. ' or if the decoded value string contains any control characters,
  103. ' the server puts the decoded value into an external tempfile and
  104. ' lists the field in this section as:
  105. '    key=<pathname> <length>
  106. ' where <pathname> is the path and name of the tempfile containing
  107. ' the decoded value string, and <length> is the length in bytes
  108. ' of the decoded value string.
  109. '
  110. ' NOTE: BE SURE TO OPEN THIS FILE IN BINARY MODE UNLESS YOU ARE
  111. '       CERTAIN THAT THE FORM DATA IS TEXT!
  112. '
  113. ' [Form Huge]
  114. ' If the raw value string is more than 65,536 bytes long, the server
  115. ' does no decoding. In this case, the server lists the field in this
  116. ' section as:
  117. '    key=<offset> <length>
  118. ' where <offset> is the offset from the beginning of the Content File
  119. ' at which the raw value string for this key is located, and <length>
  120. ' is the length in bytes of the raw value string. You can use the
  121. ' <offset> to perform a "Seek" to the start of the raw value string,
  122. ' and use the length to know when you have read the entire raw string
  123. ' into your decoder. Note that VB has a limit of 64K for strings, so
  124. '
  125. ' Examples:
  126. '
  127. '    [Form Literal]
  128. '    smallfield=123 Main St. #122
  129. '
  130. '    [Form External]
  131. '    field300chars=C:\TEMP\HS19AF6C.000 300
  132. '    fieldwithlinebreaks=C:\TEMP\HS19AF6C.001 43
  133. '
  134. '    [Form Huge]
  135. '    field230K=C:\TEMP\HS19AF6C.002 276920
  136. '
  137. ' =====
  138. ' USAGE
  139. ' =====
  140. ' Include CGI.BAS in your VB project. Set the project options for
  141. ' "Sub Main" startup. The Main() procedure is in this module, and it
  142. ' handles all of the setup of the VB CGI environment, as described
  143. ' above. Once all of this is done, the Main() calls YOUR main procedure
  144. ' which must be called CGI_Main(). The output file is open, use Send()
  145. ' to write to it. The input file is NOT open, and "huge" form fields
  146. ' have not been decoded.
  147. '
  148. ' If a Visual Basic runtime error occurs, it will be trapped and result
  149. ' in an HTTP error response being sent to the client. Check out the
  150. ' Error Handler() sub. When your program finishes, be sure to RETURN
  151. ' TO MAIN(). Don't just do an "End".
  152. '
  153. ' Finally, this is all experimental right now. It has already changed,
  154. ' once, so be prepared.
  155. '
  156. ' Have a look at the stuff below to see what's what.
  157. '
  158. '----------------------------------------------------------------------
  159. ' Author:   Robert B. Denny <rdenny@netcom.com>
  160. '           June 7, 1994
  161. '
  162. ' Revision History:
  163. '   26-May-94 rbd   Initial experimental release
  164. '   07-Jun-94 rbd   Revised keyword names and form decoding per
  165. '                   httpd 1.2b8, fixed section name of Output File.
  166. '
  167. '----------------------------------------------------------------------
  168. Option Explicit
  169. '
  170. ' ==================
  171. ' Manifest Constants
  172. ' ==================
  173. '
  174. Const MAX_CMDARGS = 8       ' Max # of command line args
  175. Const ENUM_BUF_SIZE = 4096  ' Key enumeration buffer, see GetProfile()
  176. ' These are the limits in the server
  177. Const MAX_XHDR = 100        ' Max # of "extra" request headers
  178. Const MAX_ACCTYPE = 100     ' Max # of Accept: types in request
  179. Const MAX_FORM_TUPLES = 100 ' Max # form key=value pairs
  180. Const MAX_HUGE_TUPLES = 16  ' Max # "huge" form fields
  181. '
  182. '
  183. ' =====
  184. ' Types
  185. ' =====
  186. '
  187. Type Tuple                  ' Used for Accept: and "extra" headers
  188.     key As String           ' and for holding POST form key=value pairs
  189.     value As String
  190. End Type
  191.  
  192. Type HugeTuple              ' Used for "huge" form fields
  193.     key As String           ' Keyword (decoded)
  194.     offset As Long          ' Byte offset into Content File of value
  195.     length As Long          ' Length of value, bytes
  196. End Type
  197. '
  198. '
  199. ' ================
  200. ' Global Constants
  201. ' ================
  202. '
  203. ' -----------
  204. ' Error Codes
  205. ' -----------
  206. '
  207. Global Const ERR_ARGCOUNT = 32767
  208. Global Const ERR_BAD_REQUEST = 32766        ' HTTP 400
  209. Global Const ERR_UNAUTHORIZED = 32765       ' HTTP 401
  210. Global Const ERR_PAYMENT_REQUIRED = 32764   ' HTTP 402
  211. Global Const ERR_FORBIDDEN = 32763          ' HTTP 403
  212. Global Const ERR_NOT_FOUND = 32762          ' HTTP 404
  213. Global Const ERR_INTERNAL_ERROR = 32761     ' HTTP 500
  214. Global Const ERR_NOT_IMPLEMENTED = 32760    ' HTTP 501
  215. Global Const ERR_TOO_BUSY = 32758           ' HTTP 503 (experimental)
  216. Global Const ERR_NO_FIELD = 32757           ' GetxxxField "no field"
  217.  
  218. ' ====================
  219. ' CGI Global Variables
  220. ' ====================
  221. '
  222. ' ----------------------
  223. ' Standard CGI variables
  224. ' ----------------------
  225. '
  226. Global CGI_ServerSoftware As String
  227. Global CGI_ServerName As String
  228. Global CGI_ServerPort As Integer
  229. Global CGI_RequestProtocol As String
  230. Global CGI_ServerAdmin As String
  231. Global CGI_Version As String
  232. Global CGI_RequestMethod As String
  233. Global CGI_LogicalPath As String
  234. Global CGI_PhysicalPath As String
  235. Global CGI_ExecutablePath As String
  236. Global CGI_QueryString As String
  237. Global CGI_RemoteHost As String
  238. Global CGI_RemoteAddr As String
  239. Global CGI_AuthUser As String
  240. Global CGI_TAPUser As String
  241. Global CGI_AuthType As String
  242. Global CGI_ContentType As String
  243. Global CGI_ContentLength As Long
  244. '
  245. ' ------------------
  246. ' HTTP Header Arrays
  247. ' ------------------
  248. '
  249. Global CGI_AcceptTypes(MAX_ACCTYPE) As Tuple    ' Accept: types
  250. Global CGI_NumAcceptTypes As Integer            ' # of live entries in array
  251. Global CGI_ExtraHeaders(MAX_XHDR) As Tuple      ' "Extra" headers
  252. Global CGI_NumExtraHeaders As Integer           ' # of live entries in array
  253. '
  254. ' --------------
  255. ' POST Form Data
  256. ' --------------
  257. '
  258. Global cgi_FormTuples(MAX_FORM_TUPLES) As Tuple ' POST form key=value pairs
  259. Global CGI_NumFormTuples As Integer             ' # of live entries in array
  260. Global CGI_HugeTuples(MAX_HUGE_TUPLES) As HugeTuple ' Form "huge tuples
  261. Global CGI_NumHugeTuples As Integer             ' # of live entries in array
  262.  
  263. '
  264. ' ----------------
  265. ' System Variables
  266. ' ----------------
  267. '
  268. Global CGI_ContentFile As String        ' Content/Input file pathname
  269. Global CGI_OutputFile As String         ' Output file pathname
  270. Global CGI_DebugMode As Integer         ' Script Tracing flag from server
  271. '
  272. '
  273. ' ========================
  274. ' Windows API Declarations
  275. ' ========================
  276. '
  277. ' NOTE: Declaration of GetPrivateProfileString is specially done to
  278. ' permit enumeration of keys by passing NULL key value. See GetProfile().
  279. '
  280. Declare Function GetPrivateProfileString Lib "Kernel" (ByVal lpSection As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName As String) As Integer
  281. '
  282. '
  283. ' ===============
  284. ' Local Variables
  285. ' ===============
  286. '
  287. Dim CGI_ProfileFile As String           ' Profile file pathname
  288. Dim CGI_ContentFN As Integer            ' Content/Input file number
  289. Dim CGI_OutputFN As Integer             ' Output file number
  290. Dim ErrorString As String
  291.  
  292.  
  293.  
  294.  
  295. '---------------------------------------------------------------------------
  296. '
  297. '   ErrorHandler() - Global error handler
  298. '
  299. ' If a VB runtime error occurs dusing execution of the program, this
  300. ' procedure generates an HTTP/1.0 HTML-formatted error message into
  301. ' the output file, then exits the program.
  302. '
  303. ' This should be armed immediately on entry to the program's main()
  304. ' procedure. Any errors that occur in the program are caught, and
  305. ' an HTTP/1.0 error messsage is generated into the output file. The
  306. ' presence of the HTTP/1.0 on the first line of the output file causes
  307. ' NCSA httpd for WIndows to send the output file to the client with no
  308. ' interpretation or other header parsing.
  309. '---------------------------------------------------------------------------
  310. Sub ErrorHandler (code As Integer)
  311.  
  312.     On Error Resume Next     ' Give it a good try!
  313.  
  314.     Seek #CGI_OutputFN, 1    ' Rewind output file just in case
  315.     Send ("HTTP/1.0 500 Internal Error")
  316.     Send ("MIME-Version: 1.0")
  317.     Send ("Content-type: text/html")
  318.     Send ("")
  319.     Send ("<HTML>")
  320.     Send ("<HEAD>")
  321.     Send ("<TITLE>Error in " + CGI_ExecutablePath + "</TITLE>")
  322.     Send ("<H1>Error in " + CGI_ExecutablePath + "</H1>")
  323.     Send ("</HEAD>")
  324.     Send ("<BODY>")
  325.     Send ("An internal Visual Basic error has occurred in " + CGI_ExecutablePath + ".<P>")
  326.     Send ("<PRE>" + ErrorString + "</PRE>")
  327.     Send ("<I>Please</I> note what you were doing when this problem occurred,")
  328.     Send ("so we can identify and correct it. Write down the Web page you were using,")
  329.     Send ("any data you may have entered into a form or search box, and")
  330.     Send ("anything else that may help us duplicate the problem. Then contact the")
  331.     Send ("administrator of this service: ")
  332.     Send ("<A HREF=""mailto:" & CGI_ServerAdmin & """>")
  333.     Send ("<ADDRESS><" + CGI_ServerAdmin + "></ADDRESS>")
  334.     Send ("</A></BODY></HTML>")
  335.  
  336.     Close #CGI_ContentFN
  337.     Close #CGI_OutputFN
  338.  
  339.     '======
  340.      End            ' Terminate the program
  341.     '======
  342. End Sub
  343.  
  344. '---------------------------------------------------------------------------
  345. '
  346. '   GetAcceptTypes() - Create the array of accept type structs
  347. '
  348. ' Enumerate the keys in the [Accept] section of the profile file,
  349. ' then get the value for each of the keys.
  350. '---------------------------------------------------------------------------
  351. Private Sub GetAcceptTypes ()
  352.     Dim sList As String
  353.     Dim i As Integer, j As Integer, l As Integer, n As Integer
  354.  
  355.     sList = GetProfile("Accept", "") ' Get key list
  356.     l = Len(sList)                          ' Length incl. trailing null
  357.     i = 1                                   ' Start at 1st character
  358.     n = 0                                   ' Index in array
  359.     Do While ((i < l) And (n < MAX_ACCTYPE))' Safety stop here
  360.         j = InStr(i, sList, Chr$(0))        ' J -> next null
  361.         CGI_AcceptTypes(n).key = Mid$(sList, i, j - i) ' Get Key, then value
  362.         CGI_AcceptTypes(n).value = GetProfile("Accept", CGI_AcceptTypes(n).key)
  363.         i = j + 1                           ' Bump pointer
  364.         n = n + 1                           ' Bump array index
  365.     Loop
  366.     CGI_NumAcceptTypes = n                  ' Fill in global count
  367.  
  368. End Sub
  369.  
  370. '---------------------------------------------------------------------------
  371. '
  372. '   GetArgs() - Parse the command line
  373. '
  374. ' Chop up the command line, fill in the argument vector, return the
  375. ' argument count (similar to the Unix/C argc/argv handling)
  376. '---------------------------------------------------------------------------
  377. Private Function GetArgs (argv() As String) As Integer
  378.     Dim buf As String
  379.     Dim i As Integer, j As Integer, l As Integer, n As Integer
  380.  
  381.     buf = Trim$(Command$)                   ' Get command line
  382.  
  383.     l = Len(buf)                            ' Length of command line
  384.     If l = 0 Then                           ' If empty
  385.         GetArgs = 0                         ' Return argc = 0
  386.         Exit Function
  387.     End If
  388.  
  389.     i = 1                                   ' Start at 1st character
  390.     n = 0                                   ' Index in argvec
  391.     Do While ((i < l) And (n < MAX_CMDARGS))' Safety stop here
  392.         j = InStr(i, buf, " ")              ' J -> next space
  393.         If j = 0 Then Exit Do               ' Exit loop on last arg
  394.         argv(n) = Trim$(Mid$(buf, i, j - i))' Get this token, trim it
  395.         i = j + 1                           ' Skip that blank
  396.         Do While Mid$(buf, i, 1) = " "      ' Skip any additional whitespace
  397.             i = i + 1
  398.         Loop
  399.         n = n + 1                           ' Bump array index
  400.     Loop
  401.  
  402.     argv(n) = Trim$(Mid$(buf, i, (l - i + 1)))' Get last arg
  403.     GetArgs = n + 1                         ' Return arg count
  404.  
  405. End Function
  406.  
  407. '---------------------------------------------------------------------------
  408. '
  409. '   GetExtraHeaders() - Create the array of extra header structs
  410. '
  411. ' Enumerate the keys in the [Extra Headers] section of the profile file,
  412. ' then get the value for each of the keys.
  413. '---------------------------------------------------------------------------
  414. Private Sub GetExtraHeaders ()
  415.     Dim sList As String
  416.     Dim i As Integer, j As Integer, l As Integer, n As Integer
  417.  
  418.     sList = GetProfile("Extra Headers", "") ' Get key list
  419.     l = Len(sList)                          ' Length incl. trailing null
  420.     i = 1                                   ' Start at 1st character
  421.     n = 0                                   ' Index in array
  422.     Do While ((i < l) And (n < MAX_XHDR))   ' Safety stop here
  423.         j = InStr(i, sList, Chr$(0))        ' J -> next null
  424.         CGI_ExtraHeaders(n).key = Mid$(sList, i, j - i) ' Get Key, then value
  425.         CGI_ExtraHeaders(n).value = GetProfile("Extra Headers", CGI_ExtraHeaders(n).key)
  426.         i = j + 1                           ' Bump pointer
  427.         n = n + 1                           ' Bump array index
  428.     Loop
  429.     CGI_NumExtraHeaders = n                 ' Fill in global count
  430.  
  431. End Sub
  432.  
  433. '---------------------------------------------------------------------------
  434. '
  435. '   GetFormTuples() - Create the array of POST form input key=value pairs
  436. '
  437. '---------------------------------------------------------------------------
  438. Private Sub GetFormTuples ()
  439.     Dim sList As String
  440.     Dim i As Integer, j As Integer, k As Integer
  441.     Dim l As Integer, n As Integer
  442.     Dim s As Long
  443.     Dim buf As String
  444.     Dim extName As String
  445.     Dim extFile As Integer
  446.     Dim extlen As Long
  447.  
  448.     n = 0                                   ' Index in array
  449.  
  450.     '
  451.     ' Do the easy one first: [Form Literal]
  452.     '
  453.     sList = GetProfile("Form Literal", "")  ' Get key list
  454.     l = Len(sList)                          ' Length incl. trailing null
  455.     i = 1                                   ' Start at 1st character
  456.     Do While ((i < l) And (n < MAX_FORM_TUPLES)) ' Safety stop here
  457.         j = InStr(i, sList, Chr$(0))        ' J -> next null
  458.         cgi_FormTuples(n).key = Mid$(sList, i, j - i) ' Get Key, then value
  459.         cgi_FormTuples(n).value = GetProfile("Form Literal", cgi_FormTuples(n).key)
  460.         i = j + 1                           ' Bump pointer
  461.         n = n + 1                           ' Bump array index
  462.     Loop
  463.     '
  464.     ' Now do the external ones: [Form External]
  465.     '
  466.     sList = GetProfile("Form External", "") ' Get key list
  467.     l = Len(sList)                          ' Length incl. trailing null
  468.     i = 1                                   ' Start at 1st character
  469.     extFile = FreeFile
  470.     Do While ((i < l) And (n < MAX_FORM_TUPLES)) ' Safety stop here
  471.         j = InStr(i, sList, Chr$(0))        ' J -> next null
  472.         cgi_FormTuples(n).key = Mid$(sList, i, j - i) ' Get Key, then pathname
  473.         buf = GetProfile("Form External", cgi_FormTuples(n).key)
  474.         k = InStr(buf, " ")                 ' Split file & length
  475.         extName = Mid$(buf, 1, k - 1)           ' Pathname
  476.         k = k + 1
  477.         extlen = CLng(Mid$(buf, k, Len(buf) - k + 1)) ' Length
  478.         '
  479.         ' Use feature of GET to read content in one call
  480.         '
  481.         Open extName For Binary Access Read As #extFile
  482.         cgi_FormTuples(n).value = String$(extlen, " ") ' Breathe in...
  483.         Get #extFile, , cgi_FormTuples(n).value 'GULP!
  484.         Close #extFile
  485.         i = j + 1                           ' Bump pointer
  486.         n = n + 1                           ' Bump array index
  487.     Loop
  488.  
  489.     CGI_NumFormTuples = n                   ' Number of fields decoded
  490.     n = 0                                   ' Reset counter
  491.     '
  492.     ' Finally, the [Form Huge] section. Will this ever get executed?
  493.     '
  494.     sList = GetProfile("Form Huge", "")     ' Get key list
  495.     l = Len(sList)                          ' Length incl. trailing null
  496.     i = 1                                   ' Start at 1st character
  497.     Do While ((i < l) And (n < MAX_FORM_TUPLES)) ' Safety stop here
  498.         j = InStr(i, sList, Chr$(0))        ' J -> next null
  499.         CGI_HugeTuples(n).key = Mid$(sList, i, j - i) ' Get Key
  500.         buf = GetProfile("Form Huge", CGI_HugeTuples(n).key) ' "offset length"
  501.         k = InStr(buf, " ")                 ' Delimiter
  502.         CGI_HugeTuples(n).offset = CLng(Mid$(buf, 1, (k - 1)))
  503.         CGI_HugeTuples(n).length = CLng(Mid$(buf, k, (Len(buf) - k + 1)))
  504.         i = j + 1                           ' Bump pointer
  505.         n = n + 1                           ' Bump array index
  506.     Loop
  507.     
  508.     CGI_NumHugeTuples = n                   ' Fill in global count
  509.  
  510. End Sub
  511.  
  512. '---------------------------------------------------------------------------
  513. '
  514. '   GetProfile() - Get a value or enumerate keys in CGI_Profile file
  515. '
  516. ' Get a value given the section and key, or enumerate keys given the
  517. ' section name and "" for the key. If enumerating, the list of keys for
  518. ' the given section is returned as a null-separated string, with a
  519. ' double null at the end.
  520. '
  521. ' VB handles this with flair! I couldn't believe my eyes when I tried this.
  522. '---------------------------------------------------------------------------
  523. Private Function GetProfile (sSection As String, sKey As String) As String
  524.     Dim retLen As Integer
  525.     Dim buf As String * ENUM_BUF_SIZE
  526.     If sKey <> "" Then
  527.         retLen = GetPrivateProfileString(sSection, sKey, "", buf, ENUM_BUF_SIZE, CGI_ProfileFile)
  528.     Else
  529.         retLen = GetPrivateProfileString(sSection, 0&, "", buf, ENUM_BUF_SIZE, CGI_ProfileFile)
  530.     End If
  531.     
  532.     If retLen = 0 Then
  533.         GetProfile = ""
  534.     Else
  535.         GetProfile = Left$(buf, retLen)
  536.     End If
  537.  
  538. End Function
  539.  
  540. '----------------------------------------------------------------------
  541. '
  542. ' GetShortFormField - Get the value of a "short" form field given the key
  543. '
  544. ' WARNING - Does not handle large/huge form fields.
  545. '
  546. '----------------------------------------------------------------------
  547. Function GetShortFormField (key As String) As String
  548.     Dim i As Integer
  549.  
  550.     For i = 0 To (CGI_NumFormTuples - 1)
  551.         If cgi_FormTuples(i).key = key Then
  552.             GetShortFormField = cgi_FormTuples(i).value
  553.             Exit Function           ' ** DONE **
  554.         End If
  555.     Next i
  556.     '
  557.     ' Programmer error if we get here!
  558.     '
  559.     GetShortFormField = "??no such field """ & key & """??"
  560. End Function
  561.  
  562. '----------------------------------------------------------------------
  563. '
  564. ' Get the value of a "small" form field given the key
  565. '
  566. ' Signals an error if field does not exist
  567. '
  568. '----------------------------------------------------------------------
  569. Function GetSmallField (key As String) As String
  570.     Dim i As Integer
  571.  
  572.     For i = 0 To (CGI_NumFormTuples - 1)
  573.         If cgi_FormTuples(i).key = key Then
  574.             GetSmallField = Trim$(cgi_FormTuples(i).value)
  575.             Exit Function           ' ** DONE **
  576.         End If
  577.     Next i
  578.     '
  579.     ' Field does not exist
  580.     '
  581.     Error ERR_NO_FIELD
  582. End Function
  583.  
  584. '---------------------------------------------------------------------------
  585. '
  586. '   InitializeCGI() - Fill in all of the CGI variables, etc.
  587. '
  588. ' Read the profile file name from the command line, then fill in
  589. ' the CGI globals, the Accept type list and the Extra headers list.
  590. ' Then open the input and output files.
  591. '
  592. ' Returns True if OK, False if some sort of error. See ReturnError()
  593. ' for info on how errors are handled.
  594. '
  595. ' NOTE: Assumes that the CGI error handler has been armed with On Error
  596. '---------------------------------------------------------------------------
  597. Sub InitializeCGI ()
  598.     Dim sect As String
  599.     Dim argc As Integer
  600.     Static argv(MAX_CMDARGS) As String
  601.     Dim buf As String
  602.  
  603.     CGI_DebugMode = True    ' Initialization errors are very bad
  604.     CGI_ContentFN = FreeFile
  605.     CGI_OutputFN = FreeFile
  606.  
  607.     '
  608.     ' Parse the command line. We need the profile file name (duh!)
  609.     ' and the output file name NOW, so we can return any errors we
  610.     ' trap. The error handler writes to the output file.
  611.     '
  612.     argc = GetArgs(argv())
  613.     CGI_ProfileFile = argv(0)
  614.  
  615.     sect = "CGI"
  616.     CGI_ServerSoftware = GetProfile(sect, "Server Software")
  617.     CGI_ServerName = GetProfile(sect, "Server Name")
  618.     CGI_RequestProtocol = GetProfile(sect, "Request Protocol")
  619.     CGI_ServerAdmin = GetProfile(sect, "Server Admin")
  620.     CGI_Version = GetProfile(sect, "CGI Version")
  621.     CGI_RequestMethod = GetProfile(sect, "Request Method")
  622.     CGI_LogicalPath = GetProfile(sect, "Logical Path")
  623.     CGI_PhysicalPath = GetProfile(sect, "Physical Path")
  624.     CGI_ExecutablePath = GetProfile(sect, "Executable Path")
  625.     CGI_QueryString = GetProfile(sect, "Query String")
  626.     CGI_RemoteHost = GetProfile(sect, "Remote Host")
  627.     CGI_RemoteAddr = GetProfile(sect, "Remote Address")
  628.     CGI_AuthUser = GetProfile(sect, "Authenticated User")
  629.     CGI_TAPUser = GetProfile(sect, "RFC-931 Identity")
  630.     CGI_AuthType = GetProfile(sect, "Authentication Method")
  631.     CGI_ContentType = GetProfile(sect, "Content Type")
  632.     buf = GetProfile(sect, "Content Length")
  633.     If buf = "" Then
  634.         CGI_ContentLength = 0
  635.     Else
  636.         CGI_ContentLength = CLng(buf)
  637.     End If
  638.     buf = GetProfile(sect, "Server Port")
  639.     If buf = "" Then
  640.         CGI_ServerPort = -1
  641.     Else
  642.         CGI_ServerPort = CInt(buf)
  643.     End If
  644.  
  645.     sect = "System"
  646.     CGI_ContentFile = GetProfile(sect, "Content File")
  647.     CGI_OutputFile = argv(2)
  648.     Open CGI_OutputFile For Output Access Write As #CGI_OutputFN
  649.     buf = GetProfile(sect, "Debug Mode")    ' Y or N
  650.     If (Left$(buf, 1) = "Y") Then           ' Must start with Y
  651.         CGI_DebugMode = True
  652.     Else
  653.         CGI_DebugMode = False
  654.     End If
  655.  
  656.     GetAcceptTypes          ' Enumerate Accept: types into tuples
  657.     GetExtraHeaders         ' Enumerate extra headers into tuples
  658.     GetFormTuples           ' Decode any POST form input into tuples
  659.  
  660. End Sub
  661.  
  662. '----------------------------------------------------------------------
  663. '
  664. '   main() - CGI script back-end main procedure
  665. '
  666. ' This is the main() for the VB back end. Note carefully how the error
  667. ' handling is set up, and how program cleanup is done.
  668. '----------------------------------------------------------------------
  669. Sub Main ()
  670.     On Error GoTo ErrorHandler
  671.  
  672.     InitializeCGI       ' Create the CGI environment
  673.  
  674.     '===========
  675.     CGI_Main            ' Execute the actual "script"
  676.     '===========
  677.  
  678. Cleanup:
  679.     Close #CGI_ContentFN
  680.     Close #CGI_OutputFN
  681.     Exit Sub                        ' End the program
  682. '------------
  683. ErrorHandler:
  684.     ErrorString = Error$            ' Save this NOW!
  685.     On Error GoTo 0                 ' Prevent recursion
  686.     ErrorHandler (Err)              ' Generate HTTP error result
  687.     Resume Cleanup
  688. '------------
  689. End Sub
  690.  
  691. '----------------------------------------------------------------------
  692. '
  693. '  Send() - Shortcut for writing to output file
  694. '
  695. '----------------------------------------------------------------------
  696. Sub Send (s As String)
  697.     Print #CGI_OutputFN, s
  698. End Sub
  699.  
  700.